package org.pentaho.di.core.database.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Driver;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;

public class CustomDriverClassloader extends URLClassLoader {

    static Map<String, CustomDriverClassloader> urlMap = new HashMap<>();

    private CustomDriverClassloader(URL[] urls, ClassLoader parentClassLoader) {
        super(urls, parentClassLoader);
    }

    /**
     * 获取数据库插件的类加载器
     * @param urls
     * @param parentClassLoader
     * @param pluginSingleId
     * @param testClassName
     * @return
     */
    public static ClassLoader getInstance(URL[] urls, ClassLoader parentClassLoader, String pluginSingleId, String testClassName) {
        URL location = CustomDriverClassloader.class.getProtectionDomain().getCodeSource().getLocation();
        CustomDriverClassloader customDriverClassloader = urlMap.get(pluginSingleId);
        if (!ObjectUtil.isEmpty(customDriverClassloader)) {
            return customDriverClassloader;
        }
        CustomDriverClassloader classloader = new CustomDriverClassloader(ArrayUtil.append(urls, location), parentClassLoader);
        cacheClassLoader(pluginSingleId, testClassName, classloader);
        return classloader;
    }

    /**
     * 缓存类加载器
     * @param pluginSingleId
     * @param testClassName
     * @param classloader
     */
    private static void cacheClassLoader(String pluginSingleId, String testClassName, CustomDriverClassloader classloader) {
        try {
            //TODO 如何识别缓存后的驱动变更
            if(ObjectUtil.isNotEmpty(classloader.loadClass(testClassName))){
                urlMap.put(pluginSingleId, classloader);
            }
        } catch (ClassNotFoundException e) {
            // 忽略
        }
    }


    Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        Class<?> classLoaded = classMap.get(name);
        if (classLoaded != null) {
            return classLoaded;
        }
        Class<?> findClass = null;
        try {
            findClass = findClass(name);
        } catch (Exception e) {
            // 还可以从父类查找，这个异常吞掉，如果没有父类会抛出
        } catch (LinkageError e) {
        }
        if (findClass != null) {
            classMap.put(name, findClass);
            return findClass;
        }
        return loadClassFromParent(name);
    }

    @Override
    protected Package getPackage(String name) {
        return null;
    }

    protected Class<?> loadClassFromParent(String arg0) throws ClassNotFoundException {
        Class<?> clz;
        if ((clz = getParent().loadClass(arg0)) != null) {
            resolveClass(clz);
            return clz;
        }
        throw new ClassNotFoundException("Could not find :" + arg0);
    }

    public Class<?> customManager;

    {
        try {
            customManager = loadClass("org.pentaho.di.core.database.util.CustomDriverManager");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public Enumeration<Driver> getDrivers(ClassLoader classLoader) {
        Thread.currentThread().setContextClassLoader(classLoader);
        Enumeration<Driver> drivers = null;
        try {
            customManager = loadClass("org.pentaho.di.core.database.util.CustomDriverManager");
            Constructor<?> constructor = customManager.getConstructor();
            Method getDrivers = customManager.getMethod("getDrivers", ClassLoader.class);
            Object o = constructor.newInstance();
            drivers = (Enumeration<Driver>) getDrivers.invoke(o, classLoader);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return drivers;
    }

}
